package kr.co.redcircle.loader.data;

import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;

import kr.co.redcircle.loader.utility.ByteUtil;

public class DataResultSet extends
		ArrayList<ArrayList<HashMap<Integer, String>>> {

	private static final long serialVersionUID = 7750725119227910832L;

	private DataResultMetaData metaData = null;
	private DataHeader header = null;

	private int sry_yy = 0;
	private int schoolCode = 0;
	private int orgcode = 0;

	private boolean notExistHeader = true;
	private boolean notExistMeta = true;
	private boolean notExistData = true;
	private boolean dataContFlag = false;

	private byte[] savedByteArray = null;
	private int metaIndex = 0;
	private int fieldIndex = 0;

	private int recCnt;

	public DataResultSet(int sry_yy, int schoolCode, int orgcode) {
		super();
		this.sry_yy = sry_yy;
		this.schoolCode = schoolCode;
		this.orgcode = orgcode;
	}

	public DataResultSet(int sry_yy, int schoolCode, int orgcode, String fileName) {
		super();
		this.sry_yy = sry_yy;
		this.schoolCode = schoolCode;
		this.orgcode = orgcode;
		this.buildFromFile(fileName);
	}

	public int getSry_yy() {
		return sry_yy;
	}

	public void setSry_yy(int sry_yy) {
		this.sry_yy = sry_yy;
	}

	public int getSchoolCode() {
		return schoolCode;
	}

	public void setSchoolCode(int schoolCode) {
		this.schoolCode = schoolCode;
	}

	public int getOrgcode() {
		return orgcode;
	}

	public void setOrgcode(int orgcode) {
		this.orgcode = orgcode;
	}

	public DataResultMetaData getMetaData() {
		return metaData;
	}

	private void setMetaData(DataResultMetaData metaData) {
		this.metaData = metaData;
	}

	public DataHeader getHeader() {
		return header;
	}

	private void setHeader(DataHeader header) {
		this.header = header;
	}

	public void buildFromFile(String fileName) {
		try {
			openFile(fileName);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void openFile(String fileName) throws IOException {

		byte[] b = new byte[2048];
		FileInputStream fis = new FileInputStream(fileName);
		DataInputStream in = new DataInputStream(fis);

		while (in.read(b) > 0) {
			setupHeader(b);
			setupMetaData(b);
			setupData(b);

		}

		loadData(0, this.savedByteArray, false);
	}

	private void setupHeader(byte[] b) {
		if (notExistHeader) {

			int headerDivision = ByteUtil.BytesIndexOf(b, new byte[] { 0x01,
					0x7C, 0x00, 0x00, 0x00, 0x00 }, 0);

			if (headerDivision > -1) {
				DataHeader header = new DataHeader(headerDivision);
				header.build(b);
				this.setHeader(header);
				notExistHeader = false;
			}
		}
	}

	private void setupMetaData(byte[] b) {
		if (notExistMeta) {

			int metaDivision = ByteUtil.BytesIndexOf(b, new byte[] { 0x4F,
					0x5A, 0x42, 0x49, 0x4E, 0x44, 0x45, 0x44, 0x44, 0x41, 0x54,
					0x41, 0x4D, 0x4F, 0x44, 0x55, 0x4C, 0x45 }, 0);

			if (metaDivision > -1) {
				DataResultMetaData metaData = new DataResultMetaData(metaDivision);
				int metaCount = metaData.build(b);
				this.setMetaData(metaData);
				int i = 0;

				for (i = 0; i < metaCount; i++) {
					this.add(new ArrayList<HashMap<Integer, String>>());
				}

				notExistMeta = false;
			}

		}
	}

	private void setupData(byte[] b) {
		if (notExistData) {

			int dataIndex = getDataFieldIndex(b);

			if (dataIndex > -1 || dataContFlag) {
				dataIndex += 22;
				if (this.savedByteArray != null) {
					int tgrLength = b.length + this.savedByteArray.length;
					byte[] targetArray = new byte[tgrLength];
					System.arraycopy(savedByteArray, 0, targetArray, 0,
							savedByteArray.length);
					System.arraycopy(b, 0, targetArray, savedByteArray.length,
							b.length);
					loadData(0, targetArray, true);
				} else {
					loadData(dataIndex, b, true);
				}
				dataContFlag = true;
			}
		}
	}

	private boolean loadData(int dataIndex, byte[] b, boolean backFlag) {
		int dIndex = dataIndex;

		int cnt = 0;
		while (true) {

			if (this.metaData.size() > this.metaIndex) {
				MetaData mData = this.metaData.get(this.metaIndex);

				if (this.fieldIndex == mData.size()) this.fieldIndex = 0;

				String[] targetValue = getValue(dIndex, b, 3, backFlag);
				ArrayList<HashMap<Integer, String>> metaList = this.get(this.metaIndex);
				HashMap<Integer, String> recMap = null;

				if (this.fieldIndex == 0) {

					if (this.metaData.get(this.metaIndex).getRecordCount() == recCnt) {
						this.metaIndex++;
						if (this.metaData.size() == this.metaIndex) break;
						mData = this.metaData.get(this.metaIndex);
						this.fieldIndex = 0;
						metaList = this.get(this.metaIndex);
						recCnt = 0;
					}

					recMap = new HashMap<Integer, String>();
					metaList.add(recMap);

					recCnt++;

				} else {
					recMap = metaList.get(metaList.size() - 1);
				}

				if (targetValue[0] == null)
					break;
				if (this.metaIndex == this.size() - 1)
					break;
				if (targetValue[1] != null && targetValue[1].equals("[0x00]"))
					this.fieldIndex++;
				if (targetValue[1] != null && targetValue[1].equals(""))
					targetValue[1] = "0";
				if (!(targetValue[1] != null && targetValue[1].equals("[0x00]"))) {
					String fieldName = mData.get(this.fieldIndex);
					recMap.put(this.fieldIndex, targetValue[1]);
				}

				int nextIndex = dIndex + Integer.parseInt(targetValue[0]) + 0xff;

				if (nextIndex >= b.length && backFlag) {

					dIndex += Integer.parseInt(targetValue[0]);
					int savedByteArrayLength = b.length - dIndex;
					this.savedByteArray = new byte[savedByteArrayLength];
					System.arraycopy(b, dIndex, savedByteArray, 0, savedByteArrayLength);
					this.fieldIndex++;
					break;

				} else {
					dIndex += Integer.parseInt(targetValue[0]);
				}

				if (mData.getFieldCount() <= this.fieldIndex)
					this.fieldIndex = 0;
				else
					this.fieldIndex++;
			} else {
				break;
			}
		}

		return false;
	}

	private String[] getValue(int dIndex, byte[] b, int valueLength,
			boolean backFlag) {
		String[] retString = new String[2];
		int vLength = getValueLength(dIndex, b, valueLength);

		if (vLength > 0xff) {

			valueLength -= 1;
			vLength = getValueLength(dIndex, b, valueLength);

			if (b.length < dIndex + vLength + valueLength)
				return retString;
			byte byteTarget = b[dIndex + vLength + valueLength];

			if ((byteTarget != 0x00) && (byteTarget != 0x01) && backFlag) {
				valueLength += 1;
				vLength = getValueLength(dIndex, b, valueLength);
			}
		}

		if (vLength < 0) {
			retString[0] = 1 + "";
			return retString;
		} else {

			byte[] valueArray = getByteArray(dIndex + valueLength, b, vLength);

			if (valueArray != null) {
				retString[0] = (valueLength + valueArray.length) + "";

				try {
					if (valueArray.length == 1 && valueArray[0] == 0x00) {
						retString[1] = "[0x00]";
					} else {
						retString[1] = new String(valueArray, "utf-8");
					}
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}
			}
		}
		return retString;
	}

	private int getValueLength(int offset, byte[] b, int valueLength) {

		byte[] sizeArray = getByteArray(offset, b, valueLength);

		if (sizeArray[0] == 0x01)
			return -1;
		return ByteUtil.byteArrayToInt(sizeArray);

	}

	private byte[] getByteArray(int dataIndex, byte[] b, int byteLength) {
		byte[] sizeArray = new byte[byteLength];
		if (b.length > dataIndex + byteLength) {
			System.arraycopy(b, dataIndex, sizeArray, 0, sizeArray.length);
			return sizeArray;
		} else {
			return null;
		}
	}

	private int getDataFieldIndex(byte[] b) {

		byte[] yyByte = getDataFieldArray(this.sry_yy);
		byte[] scByte = getDataFieldArray(this.schoolCode);
		byte[] orByte = getDataFieldArray(this.orgcode);

		byte[] searchTarget = new byte[yyByte.length + scByte.length
				+ orByte.length];

		System.arraycopy(yyByte, 0, searchTarget, 0, yyByte.length);
		System.arraycopy(scByte, 0, searchTarget, yyByte.length, scByte.length);
		System.arraycopy(orByte, 0, searchTarget,
				yyByte.length + scByte.length, orByte.length);

		return ByteUtil.BytesIndexOf(b, searchTarget, 0);

	}

	private byte[] getDataFieldArray(int target) {
		String tmpString = target + "";
		int tmpSize = tmpString.length();
		byte[] retByteArray = new byte[tmpSize + 3];
		byte[] tmpByteArray = ByteUtil.intToByteArray(tmpSize);
		System.arraycopy(tmpByteArray, 1, retByteArray, 0, 3);

		char[] tmpCharArray = tmpString.toCharArray();

		int i = 0;
		for (i = 0; i < tmpCharArray.length; i++) {
			retByteArray[i + 3] = (byte) tmpCharArray[i];
		}

		return retByteArray;
	}

}
